// #include <emscripten.h> // For emscripten_get_device_pixel_ratio()
// #include <emscripten/html5.h> // For Emscripten HTML5 WebGL context creation API
// #include <webgl/webgl1.h> // For Emscripten WebGL API headers (see also webgl/webgl1_ext.h and webgl/webgl2.h)
// #include <string.h> // For NULL and strcmp()
// #include <assert.h> // For assert()

// void upload_unicode_char_to_texture(int unicodeChar, int charSize, int applyShadow);
// void load_texture_from_url(GLuint texture, const char *url, int *outWidth, int *outHeight);

// static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE glContext;
// static GLuint quad, colorPos, matPos, solidColor;
// static float pixelWidth, pixelHeight;

// static GLuint compile_shader(GLenum shaderType, const char *src)
// {
//    GLuint shader = glCreateShader(shaderType);
//    glShaderSource(shader, 1, &src, NULL);
//    glCompileShader(shader);
//    return shader;
// }

// static GLuint create_program(GLuint vertexShader, GLuint fragmentShader)
// {
//    GLuint program = glCreateProgram();
//    glAttachShader(program, vertexShader);
//    glAttachShader(program, fragmentShader);
//    glBindAttribLocation(program, 0, "pos");
//    glLinkProgram(program);
//    glUseProgram(program);
//    return program;
// }

// static GLuint create_texture()
// {
//   GLuint texture;
//   glGenTextures(1, &texture);
//   glBindTexture(GL_TEXTURE_2D, texture);
//   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
//   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//   return texture;
// }

// void init_webgl(int width, int height)
// {
//   double dpr = emscripten_get_device_pixel_ratio();
//   emscripten_set_element_css_size("canvas", width / dpr, height / dpr);
//   emscripten_set_canvas_element_size("canvas", width, height);

//   EmscriptenWebGLContextAttributes attrs;
//   emscripten_webgl_init_context_attributes(&attrs);
//   attrs.alpha = 0;
//   attrs.majorVersion= 2;

//   glContext = emscripten_webgl_create_context("canvas", &attrs);
//   assert(glContext);
//   emscripten_webgl_make_context_current(glContext);

//   pixelWidth = 2.f / width;
//   pixelHeight = 2.f / height;

//   static const char vertex_shader[] =
//     "attribute vec4 pos;"
//     "varying vec2 uv;"
//     "uniform mat4 mat;"
//     "void main(){"
//       "uv=pos.xy;"
//       "gl_Position=mat*pos;"
//     "}";
//   GLuint vs = compile_shader(GL_VERTEX_SHADER, vertex_shader);

//   static const char fragment_shader[] =
//     "precision lowp float;"
//     "uniform sampler2D tex;"
//     "varying vec2 uv;"
//     "uniform vec4 color;"
//     "void main(){"
//       "gl_FragColor=color*texture2D(tex,uv);"
//     "}";
//   GLuint fs = compile_shader(GL_FRAGMENT_SHADER, fragment_shader);

//   GLuint program = create_program(vs, fs);
//   colorPos = glGetUniformLocation(program, "color");
//   matPos = glGetUniformLocation(program, "mat");
//   glEnable(GL_BLEND);
//   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

//   glGenBuffers(1, &quad);
//   glBindBuffer(GL_ARRAY_BUFFER, quad);
//   const float pos[] = { 0, 0, 1, 0, 0, 1, 1, 1 };
//   glBufferData(GL_ARRAY_BUFFER, sizeof(pos), pos, GL_STATIC_DRAW);
//   glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
//   glEnableVertexAttribArray(0);

//   solidColor = create_texture();
//   unsigned int whitePixel = 0xFFFFFFFFu;
//   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &whitePixel);
// }

// typedef void (*tick_func)(double t, double dt);

// static EM_BOOL tick(double time, void *userData)
// {
//   static double t0;
//   double dt = time - t0;
//   t0 = time;
//   tick_func f = (tick_func)(userData);
//   f(time, dt);
//   return EM_TRUE;
// }

// void clear_screen(float r, float g, float b, float a)
// {
//   glClearColor(r, g, b, a);
//   glClear(GL_COLOR_BUFFER_BIT);
// }

// static void fill_textured_rectangle(float x0, float y0, float x1, float y1, float r, float g, float b, float a, GLuint texture)
// {
//   float mat[16] = { (x1-x0)*pixelWidth, 0, 0, 0, 0, (y1-y0)*pixelHeight, 0, 0, 0, 0, 1, 0, x0*pixelWidth-1.f, y0*pixelHeight-1.f, 0, 1};
//   glUniformMatrix4fv(matPos, 1, 0, mat);
//   glUniform4f(colorPos, r, g, b, a);
//   glBindTexture(GL_TEXTURE_2D, texture);
//   glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// }

// void fill_solid_rectangle(float x0, float y0, float x1, float y1, float r, float g, float b, float a)
// {
//   fill_textured_rectangle(x0, y0, x1, y1, r, g, b, a, solidColor);
// }

// typedef struct Texture
// {
//   // Image
//   char *url;
//   int w, h;

//   GLuint texture;
// } Texture;

// #define MAX_TEXTURES 256
// static Texture textures[MAX_TEXTURES];// = {};

// static Texture *find_or_cache_url(const char *url)
// {
//   for(int i = 0; i < MAX_TEXTURES; ++i) // Naive O(n) lookup for tiny code size
//     if (!strcmp(textures[i].url, url))
//       return textures+i;
//     else if (!textures[i].url)
//     {
//       textures[i].url = strdup(url);
//       textures[i].texture = create_texture();
//       load_texture_from_url(textures[i].texture, url, &textures[i].w, &textures[i].h);
//       return textures+i;
//     }
//   return 0; // fail
// }

// void fill_image(float x0, float y0, float scale, float r, float g, float b, float a, const char *url)
// {
//   Texture *t = find_or_cache_url(url);
//   fill_textured_rectangle(x0, y0, x0 + t->w * scale, y0 + t->h * scale, r, g, b, a, t->texture);
// }

// typedef struct Glyph
// {
//   // Font
//   unsigned int ch;
//   int charSize;
//   int shadow;

//   GLuint texture;
// } Glyph;

// #define MAX_GLYPHS 256
// static Glyph glyphs[MAX_GLYPHS];// = {};
// static Glyph *find_or_cache_character(unsigned int ch, int charSize, int shadow)
// {
//   for(int i = 0; i < MAX_TEXTURES; ++i) // Naive O(n) lookup for tiny code size
//     if (glyphs[i].ch == ch && glyphs[i].charSize == charSize && glyphs[i].shadow == shadow)
//       return glyphs+i;
//     else if (!glyphs[i].ch)
//     {
//       glyphs[i].ch = ch;
//       glyphs[i].charSize = charSize;
//       glyphs[i].shadow = shadow;
//       glyphs[i].texture = create_texture();
//       upload_unicode_char_to_texture(ch, charSize, shadow);
//       return glyphs+i;
//     }
//   return 0; // fail
// }

// void fill_char(float x0, float y0, float r, float g, float b, float a, unsigned int ch, int charSize, int shadow)
// {
//   fill_textured_rectangle(x0, y0, x0+charSize, y0+charSize, r, g, b, a, find_or_cache_character(ch, charSize, shadow)->texture);
// }

// void fill_text(float x0, float y0, float r, float g, float b, float a, const char *str, float spacing, int charSize, int shadow)
// {
//   while(*str)
//   {
//     fill_char(x0, y0, r, g, b, a, *str++, charSize, shadow);
//     x0 += spacing;
//   }
// }
